#pragma once
#include "CTokens.h"
#include "CScripts.h"
#include "CMacros.h"

#define MAX_LINE_LENGTH 1024
#define MAX_PARSE_LINE 512
#define MAX_INLINE_OPERATIONS 40
#define MAX_ASM_STRUCTS 800
#define MAX_ASM_LABEL_PTRS	128
#define IDENTIFIERS_PER_LINE 64
#define TOKENS_PER_LINE 50
#define COMPILATION_BUFFER_LENGTH 1024

enum eParseType
{
	PARSE_TYPE_SUBSCRIPT,
	PARSE_TYPE_SCRIPT,
};
enum eScriptIdentifier
{
	IDENTIFIER_NONE,
	IDENTIFIER_TYPE,				// INT, VAR_NAME, LVAR_CONST etc.
	IDENTIFIER_LABEL,				// mylabel:
	IDENTIFIER_VARIABLE,			// identifier previously declared as variable
	IDENTIFIER_COMMAND,				// identifier loaded from commands.ini
	IDENTIFIER_UNKNOWN = 255,
};
enum eStringSpecialChar : char
{
	STRCHAR_BEGIN = '\xD0',
	STRCHAR_SPACE,
	STRCHAR_SLASH,
	STRCHAR_COMMA,
	STRCHAR_LBRACE,
	STRCHAR_RBRACE,
	STRCHAR_TAB,
	STRCHAR_COLON,
	STRCHAR_BSLASH,
	STRCHAR_QUOTE,
	STRCHAR_END,
};
enum
{
	EVAL_TYPE_MATH,
	EVAL_TYPE_LOGIC,
};

enum eBuiltInMacro
{
	PRESET_MACRO_SCRAMBL,
	PRESET_MACRO_SCRAMBL_VERSION,
	MAX_BUILT_IN_MACROS
};

class CStreamedScriptInfo
{
public:
	int m_nIndex;
	DWORD m_dwMainOffset;
	int m_nSize;
	char m_szName[20];
};

class CParser
{
	friend class CCompiler;
	friend class CScript;
	friend class CScriptFile;

	static eTokenType			s_eTokenType,
								s_ePredictedNextType;
	static char					s_pLineBuffer[MAX_LINE_LENGTH],
								s_szIdentifier[MAX_IDENTIFIER_NAME+4],
								s_szStringLiteral[2048],
							*	s_pLineBegin,
							*	s_pLineCurrent,
							*	s_pLineEnd,
			 				*	s_pLineTokens[IDENTIFIERS_PER_LINE];
	static const char		*	s_pCurrentToken;
	static CToken			*	s_pToken,
							*	s_pLogicToken,
							*	s_pNullToken,
							*	s_pLastToken;
	static DWORD				s_dwNumBytes;
	static const CTypeBase	*	s_pDeclaredVarType;
	static int					s_nNumCodeLines,
								s_nNumTokens,
								s_nNumRegisteredTokens,
								s_nNumInlineOperations,
								s_nNumStringLiteralChars,
								s_nNumAsmStructs,
								s_nMacroTokenIdx,
								s_bLogicalOperator,
								s_nLastParseID,
								s_nNumIFs,
								s_nNumREPEATs,
								s_nNumWHILEs,
								s_nNumConditions,		// pointer to update the IF token while conditions are being parsed
								s_nControlStructDepth,
								s_nNumComments,
								s_nConstructID,
								s_nHexBeginLine,
								s_nAsmBeginLine,
								s_nAsmStructID,
								s_nNumSwitchCases,
								s_nNumMacroParamsPassed,
								s_nSwitchStartLine,
								s_nSwitchDepth,
								s_nBaseVarTokenIdx;
	static bool					s_bLabelIsDefined,
								s_bLineContinuesConditions,
								s_bCommentActive,
								s_bStringActive,
								s_bJustOutOfString,
								s_bIsInPreprocessorDirective,
								s_bIsInControlStruct,
								s_bIsInConstStruct,
								s_bIsInHexStruct,
								s_bIsInAsmStruct,
								s_bIsDeclaringMacro,
								s_bLineHasLabel,
								s_bWeAreBeingFedByMacro,
								s_bDontFeedFromMacro,
								s_bMacroIsFunctiony,
								s_bInPreprocessorControlStruct,
								s_bLogicalOperatorNot,
								s_bParseOnlyMode,
								s_bExpectAnything,
								s_bLoopCurrentLine,
								s_bNoPredefined;

	// For preprocessor command/logic parsing
	static bool					s_bPreprocessorLogicHistory[256];		// active preprocessor blocks
	static int					s_nPreprocessorControlDepth;

	static CList<CToken>		s_MaybeLabelTokens;

protected:
	static struct sAsmStruct
	{
		bool		m_bIsAssembled;
		int			m_nStartLine;
		int			m_nEndLine;
		int			m_nNumLabels;
		int			m_nNumExtLabelRefs;
		DWORD		m_dwAssembledSize;
		DWORD		m_dwActualSize;
		void	*	m_pData;
		CLabel	*	m_pLabelList[MAX_ASM_LABEL_PTRS];
	} s_AsmStructs[MAX_ASM_STRUCTS], * s_pAsmStruct;

private:
#pragma pack(push,1)
	static struct sAsmLabelOffset
	{
		BYTE op;
		int val;
	};
#pragma pack(pop)

	static eTokenType		GetTokenData(eTokenType = TOKEN_NONE);
	static eTokenType		GetTokenData(const char *, eTokenType = TOKEN_NONE);
	static eTokenType		StorePredictedIdentifier(const char *, eTokenType = TOKEN_NONE);
	static eTokenType		StoreIdentifierValue(const char *);
	static void				PreparseLine();
	static void				PreparseToken();
	static void				ProcessLine();
	static void				ProcessDirective();
	static CToken		*	ProcessConstruct();
	static void				ProcessEndOfConditionList();
	static CToken		*	ProcessHexToken();
	static void				ProcessTypeName();
	static CToken		*	ProcessCommand();
	static CToken		*	ProcessOperator();
	static void				ProcessArgs(CToken *);
	static CToken		*	ProcessLabel(bool bCreate=false);
	static CToken		*	ProcessValue();
	static CToken		*	ProcessVariable();
	static CToken		*	ProcessString();
	static void				ProcessCommandArg(CToken *, const CCommandArg *);
	static void				CalculateOffsets();
	static void				DoLineFinalPass();
	static bool				ParseMath(int = 0);
	static void				ParseLine();
	static bool				ParseConstruct(CToken *, CToken *);
	static void				ParseCommand(CToken *, const CCommand *, int = 0);
	static bool				ParseCommandToken(CToken *, CToken *, const CType *, int);
	static void				ParseSwitchBegin();
	static void				ParseSwitchEnd();
	static void				ParseCaseBegin();
	static void				TranslateToken(CToken *);
	static void				DoRegisterCommand();
	static void				DoIncludeFile();
	static void				AssembleASM();
	static void				CreateListFile();

	static void Init()
	{
		s_bPreprocessorLogicHistory[0] = true;
		s_bCommentActive = false;
		s_bStringActive = false;
		s_bJustOutOfString = false;
		s_bIsInControlStruct = false;
		s_bIsInConstStruct = false;
		s_bIsInHexStruct = false;
		s_bIsInAsmStruct = false;
		s_bInPreprocessorControlStruct = false;
		s_bIsDeclaringMacro = false;
		s_bLogicalOperatorNot = false;
		s_bExpectAnything = false;
		s_bNoPredefined = false;
		s_dwNumBytes = 0;
		s_nPreprocessorControlDepth = 0;
		s_nControlStructDepth = 0;
		s_nLastParseID = -1;
		s_nNumCodeLines = 0;
		s_nNumComments = 0;
		s_nNumIFs = 0;
		s_nNumLinesParsed = 0;
		s_nNumSwitchCases = 0;
		s_nNumTokens = 0;

		s_pNullToken = new CToken(TOKEN_NONE);
		s_pNullToken->IsSkippedDuringCompilation(true);
		s_pToken = nullptr;
		s_pCommand = nullptr;
		s_pCommandArg = nullptr;
		s_pLabel = nullptr;
		s_pVariable = nullptr;
		s_pOperator = nullptr;
		s_pControlStructure = nullptr;
		s_pMacro = nullptr;
		s_pDeclaredMacro = nullptr;
		s_pMacroParam = nullptr;

		// #define __SCRAMBL
		auto pMacro = CMacros::Add("__SCRAMBL");
		pMacro->FinishDeclaring();

		// #define __SCRAMBL_VER xx(.)xx(.)xx(.)xx
		pMacro = CMacros::Add("__SCRAMBL_VER");
		auto pToken = new CToken(TOKEN_VALUE, tostr(g_dwVersion));
		pToken->SetValue(g_dwVersion);
		pToken->SetValueType(CSyntax::GetType(TYPE_INT));
		pMacro->AddToken(pToken);
		pMacro->FinishDeclaring();
	}
	static void Shutdown()
	{
		delete s_pNullToken;
	}

	static void ClearIdentifiers()
	{
		for(; s_nNumTokens; s_nNumTokens--)
			s_pLineTokens[s_nNumTokens] = nullptr;
	}

	static void SetTokenOnLine(int line, int index, CToken *pToken)
	{
		if(line < MAX_LINES && index < MAX_TOKENS) s_pTokens[line][index] = pToken;
	}
	static void ShiftTokensRight(int from=0, int to=0, int by=1)
	{
		if(!to) to = s_nNumRegisteredTokens;
		if(to > MAX_TOKENS) to = MAX_TOKENS;
		for(; by; by--)
		{
			for(int index = to; index > from; index--)
			{
				s_pTokens[s_nNumLinesParsed][index] = s_pTokens[s_nNumLinesParsed][index-1];
			}
		}
	}
	static int RecallTokenIndexOfType(eTokenType type, int from=0)
	{
		for(int i = from; i < MAX_TOKENS; i++)
		{
			CToken * pToken;
			if(!(pToken = s_pTokens[s_nNumLinesParsed][i])) break;
			if(pToken->GetType() == type)
				return i;
		}
		return -1;
	}
	static bool ReadyNextToken()
	{
		while(true)
		{
			if(IsLineAtEnd()) break;
			while(!*s_pLineCurrent) ++s_pLineCurrent;
			ProcessComment();
			if(IsLineAtEnd()) break;
			return true;
		}
		return false;
	}
	static void ProcessStringLiteral()
	{
		if(s_bIsInAsmStruct) return;
		if(*s_pLineCurrent != STRCHAR_BEGIN) return;
		for(int i=0; s_pLineCurrent[i] && s_pLineCurrent[i-1] != STRCHAR_END; ++i)
		{
			switch(s_pLineCurrent[i])
			{
			case STRCHAR_BEGIN: break;
			case STRCHAR_END:
				s_pLineCurrent[i] = 0;
				break;
			case STRCHAR_SPACE:
				s_pLineCurrent[i] = ' ';
				break;
			case STRCHAR_SLASH:
				s_pLineCurrent[i] = '/';
				break;
			case STRCHAR_COMMA:
				s_pLineCurrent[i] = ',';
				break;
			case STRCHAR_LBRACE:
				s_pLineCurrent[i] = '(';
				break;
			case STRCHAR_RBRACE:
				s_pLineCurrent[i] = ')';
				break;
			case STRCHAR_TAB:
				s_pLineCurrent[i] = '\t';
				break;
			case STRCHAR_COLON:
				s_pLineCurrent[i] = ':';
				break;
			case STRCHAR_BSLASH:
				for(int j=i; s_pLineCurrent[j] && s_pLineCurrent[j] != STRCHAR_END; j++)
					s_pLineCurrent[j] = s_pLineCurrent[j+1];
				switch(s_pLineCurrent[i])
				{
				case '0':
					s_pLineCurrent[i] = '\0';
					break;
				case 'x':
					// \x09 etc.
					{
						if(!IsHex(s_pLineCurrent[i+1]))
							throw("Invalid character escape sequence");
						char hex[5];
						hex[0] = '0';
						hex[1] = 'x';
						hex[2] = s_pLineCurrent[i+1];
						if(IsHex(s_pLineCurrent[i+2]))
						{
							hex[3] = s_pLineCurrent[i+2];
							hex[4] = '\0';
						}
						else hex[3] = '\0';
						int c = StringToInt(hex);
						for(int j=i; s_pLineCurrent[j] && s_pLineCurrent[j] != STRCHAR_END; j++)
							s_pLineCurrent[j] = s_pLineCurrent[j+1];
						if(IsHex(s_pLineCurrent[i+1]))
						{
							for(int j=i; s_pLineCurrent[j] && s_pLineCurrent[j] != STRCHAR_END; j++)
								s_pLineCurrent[j] = s_pLineCurrent[j+1];
						}
						s_pLineCurrent[i] = c;
					}
					break;
				case 't':
					s_pLineCurrent[i] = '\t';
					break;
				case 'n':
					s_pLineCurrent[i] = '\n';
					break;
				case 'r':
					s_pLineCurrent[i] = '\r';
					break;
				case STRCHAR_QUOTE:
					s_pLineCurrent[i] = '"';
					break;
				case STRCHAR_BSLASH:
					s_pLineCurrent[i] = '\\';
					break;
				default:
					Warning("Unrecognized character escape sequence");
					break;
				}
				break;
			}
		}
	}
	static int ReadToken()
	{
		while(true)
		{
			if(!ReadyNextToken()) break;
			
			if(s_bIsInAsmStruct)
			{
				s_pLineCurrent = strtok(s_nNumTokens ? NULL : s_pLineCurrent, "\t\n\r ");
			}
			else
			{
				if(!s_bIsInPreprocessorDirective && *s_pLineBegin != '#')
				{
					for(int i=0;s_pLineCurrent[i];i++)
						if(s_pLineCurrent[i] == '(' || s_pLineCurrent[i] == ')' || s_pLineCurrent[i] == ',')
							s_pLineCurrent[i] = ' ';

					s_pLineCurrent = strtok(s_nNumTokens ? NULL : s_pLineCurrent, "\t\n\r ");
				}
				else
				{
					if(*s_pLineCurrent == '(')
					{
						if(s_bIsDeclaringMacro)
						{
							s_pLineCurrent = strtok(s_nNumTokens ? NULL : s_pLineCurrent, "\t\n\r ");
						}
						else
						{
							// Integer constant expression, maybe...
							if(!(s_pLineCurrent = strtok(s_nNumTokens ? NULL : s_pLineCurrent, ")")))
								throw("Expected ')'");
						}
					}
					else
					{
						char * pStr;
						if(s_bIsDeclaringMacro && (pStr = strpbrk(s_pLineCurrent + strlen(s_pLineCurrent) + 1, " \t\r\n(")) && *pStr == '(')
						{
							s_bMacroIsFunctiony = true;
							s_pLineCurrent = strtok(s_nNumTokens ? NULL : s_pLineCurrent, "(,)\t\n\r");
						}
						else s_pLineCurrent = strtok(s_nNumTokens ? NULL : s_pLineCurrent, s_bIsDeclaringMacro ? "\t\n\r " :"()\t\n\r ");
					}
				}
			}
			
			if(IsLineAtEnd()) continue;

			ProcessStringLiteral();

			if(*s_pLineCurrent != STRCHAR_BEGIN && IsSpace(*s_pLineCurrent)) continue;

			if(s_nNumTokens)
			{
				ProcessComment();
				if(IsLineAtEnd()) return -1;
			}

			s_pLineTokens[s_nNumTokens] = *s_pLineCurrent == STRCHAR_BEGIN ? s_pLineCurrent + 1 : s_pLineCurrent;
			s_pCurrentToken = s_pLineTokens[s_nNumTokens];
			return s_nNumTokens++;
		}
		return -1;
	}
	static char * ReadLine(CScriptFile *pScriptFile)
	{
		while(!pScriptFile->HasEnded() && pScriptFile->ReadLine(s_pLineBuffer, sizeof(s_pLineBuffer)))
		{
 			s_nNumCodeLines++;
			s_pLineBuffer[sizeof(s_pLineBuffer) - 1] = 0;
			s_pLineBuffer[sizeof(s_pLineBuffer) - 2] = 0;
				
			for(s_pLineBegin = s_pLineBuffer; IsSpace(*s_pLineBegin); s_pLineBegin++);

			s_pLineCurrent = s_pLineBegin;
			s_pLineEnd = strlen(s_pLineBegin) + s_pLineBegin;

			ProcessComment();

			if(!IsLineAtEnd() && !s_bCommentActive)
				return s_pLineBegin;
		}
		return nullptr;
	}

	inline static bool IsLineAtEnd()						{return s_pLineCurrent <= 0 || s_pLineCurrent >= s_pLineEnd;}
	inline static bool CheckLineComment()					{return s_pLineCurrent[0] == '/' && s_pLineCurrent[1] == '/';}
	inline static bool CheckBlockCommentBegin()				{return s_pLineCurrent[0] == '/' && s_pLineCurrent[1] == '*';}
	inline static bool CheckBlockCommentEnd()				{return s_pLineCurrent[0] == '*' && s_pLineCurrent[1] == '/';}
	inline static bool CheckIdentifierChar()				{return IsIdentifierChar(s_pLineCurrent[0]);}

	// R* likes .'s, too
	inline static bool IsIdentifierChar(int c)				{return IsAlphanumeric(c) || c == '_' || c == '[' || c == ']' || c == '.';}
	inline static bool IsOperatorChar(int c)				{return CSyntax::IsOperatorChar(c);}

	inline static const char * GetFirstNonIdentifierChar()
	{
		const char *szString = s_pLineBegin;
		while(*szString && IsIdentifierChar(*szString)) szString++;
		return *szString ? szString : nullptr;
	}

	inline static void PushCodePosition()
	{
		s_nPushedLinesParsed = s_nNumLinesParsed;
		s_nPushedRegisteredTokens = s_nNumRegisteredTokens;
	}
	inline static void PopCodePosition()
	{
		s_nNumLinesParsed = s_nPushedLinesParsed;
		s_nNumRegisteredTokens = s_nPushedRegisteredTokens;
	}

	static void ProcessComment()
	{
		while(!IsLineAtEnd())
		{
			if(!s_bCommentActive)
			{
				if(CheckLineComment())
				{
					s_pLineCurrent = s_pLineEnd;
					break;
				}
				else if(CheckBlockCommentBegin())
				{
					// Ah, nestable comments :)
					s_bCommentActive = 1;
					s_nNumComments++;
					s_pLineCurrent += 2;
				}
				else break;
			}
			else
			{
				while(!IsLineAtEnd())
				{
					if(CheckBlockCommentBegin())
					{
						s_nNumComments++;
						s_pLineCurrent++;
					}
					else if(CheckBlockCommentEnd())
					{
						s_pLineCurrent++;
						s_nNumComments--;

						if(!s_nNumComments)
						{
							s_bCommentActive = 0;
							s_pLineCurrent++;
							s_pLineCurrent = ltrim(s_pLineCurrent);
							break;
						}
					}
					s_pLineCurrent++;
				}
			}
		}
	}

	static int EvaluateExpression(char *&expr, int depth=0)
	{
		// Integer constant expression evaluator function (thats the most long words I've typed in a row ever...)

		eExprOp		eOperation = EXPR_OP_INVALID,
					ePostUnary = EXPR_OP_INVALID,
					ePreUnary = EXPR_OP_INVALID;

		CMacro *pMacro;
		bool	bLogicalOp = false,
				bLogicalNot = false,
				bCont = true,
				bGotLVal = false,
				bAssign = false,
				bFeedFromMacro = false;
		int		lVal = 0,
				rVal = 0,
				nMacroIdx;

		do
		{
			while(IsSpace(*expr)) ++expr;
			
			// If we're still feeding from a macro, we'll just go straight to the identifier case
			switch(bFeedFromMacro ? '\0' : *expr)
			{
			case '!':
				if(expr[1] == '=') {
					eOperation = EXPR_OP_INEQUAL;
					expr++;
				} else {
					if(!bCont) throw("Expected end of expression (missing && or || connectors?)");
					bLogicalNot = !bLogicalNot;
				}
				++expr;
				break;
			case ')':
				if(!!!!!!!!!!!!!!!!!!!depth)
					throw("Unexpected character ')'");
				++expr;
				return lVal;
			case '(':
				if(!bCont) throw("Expected end of expression (missing && or || connectors?)");
				do ++depth;
				while(*++expr == '(');
				if(bGotLVal) rVal = EvaluateExpression(expr, depth);
				else
				{
					lVal = EvaluateExpression(expr, depth);
					bGotLVal = true;
				}
				--depth;
				bCont = false;
				break;
			case '|':
				if(expr[1] == '|') {
					if(bCont) throw("Expected expression");
					bLogicalOp = true;
					bCont = true;
					++expr;
				} else if(expr[-1] != '|') eOperation = EXPR_OP_BIT_OR;
				++expr;
				break;
			case '&':
				if(expr[1] == '&') {
					if(bCont) throw("Expected expression");
					bLogicalOp = false;
					bCont = true;
					++expr;
				} else if(expr[-1] != '&') eOperation = EXPR_OP_BIT_AND;
				++expr;
				break;
			case '^':
				eOperation = EXPR_OP_BIT_XOR;
				++expr;
				break;
			case '~':
				if(bCont || eOperation) ePreUnary = EXPR_OP_BIT_NOT;
				else ePostUnary = EXPR_OP_BIT_NOT;
				++expr;
				break;
			case '=':
				if(eOperation) bAssign = true;
				else
				{
					if(expr[1] == '=') {
						eOperation = EXPR_OP_EQUAL;
						++expr;
					} else if(expr[1] == '<') {
						eOperation = EXPR_OP_EQGREATER;
						++expr;
					} else if(expr[1] == '>') {
						eOperation = EXPR_OP_EQLESSER;
						++expr;
					} else eOperation = EXPR_OP_ASSIGN;
				}
				++expr;
				break;
			case '*':
				eOperation = EXPR_OP_MULTIPLY;
				++expr;
				break;
			case '/':
				eOperation = EXPR_OP_DIVIDE;
				++expr;
				break;
			case '+':
				if(expr[1] == '+') {
					if(bCont || eOperation) ePreUnary = EXPR_OP_INC;
					else ePostUnary = EXPR_OP_INC;
					++expr;
				} else eOperation = EXPR_OP_ADD;
				++expr;
				break;
			case '-':
				if(expr[1] == '-') {
					if(bCont || eOperation) ePreUnary = EXPR_OP_DEC;
					else ePostUnary = EXPR_OP_DEC;
					++expr;
				} else eOperation = EXPR_OP_SUBTRACT;
				++expr;
				break;
			case '<':
				if(expr[1] == '<') {
					eOperation = EXPR_OP_BIT_LSH;
					++expr;
				} else if(expr[1] == '=') {
					eOperation = EXPR_OP_EQGREATER;
					++expr;
				} else eOperation = EXPR_OP_GREATER;
				++expr;
				break;
			case '>':
				if(expr[1] == '<') {
					eOperation = EXPR_OP_BIT_RSH;
					++expr;
				} else if(expr[1] == '=') {
					eOperation = EXPR_OP_EQLESSER;
					++expr;
				} else eOperation = EXPR_OP_LESSER;
				++expr;
				break;
			default:
				int val;

				if(!bCont && !eOperation) throw("Expected end of expression (missing && or || connectors?)");
				
				if(!strncmp(expr, "DEFINED", 7))
				{
					if(!(expr = strtok(expr, "("))) throw("Expected '('");
					if(!(expr = strtok(nullptr, ")"))) throw("Expected ')'");

					if(!bGotLVal) lVal = true;

					bool bPrevDontFeed = s_bDontFeedFromMacro;
					s_bDontFeedFromMacro = true;

					switch(StoreIdentifierValue(expr))
					{
					case TOKEN_STRING:
					case TOKEN_UNKNOWN:
					case TOKEN_NONE:
						val = CSyntax::EvalLogic(lVal ? true : false, bLogicalNot, bLogicalOp, false);
						break;
					default:
						val = CSyntax::EvalLogic(lVal ? true : false, bLogicalNot, bLogicalOp, true);
						break;
					}

					s_bDontFeedFromMacro = bPrevDontFeed;

					if(bGotLVal) rVal = val;
					else lVal = val;

					while(*expr++);
				}
				else
				{
					CToken *pToken;
					
					if(bFeedFromMacro)
					{
						if(nMacroIdx >= pMacro->GetNumTokens())
						{
							if(pMacro->m_pInheritedByMacro)
							{
								pMacro = pMacro->m_pInheritedByMacro;
								nMacroIdx = pMacro->m_nInheritedMacroIdx;
								pToken = pMacro->GetToken(nMacroIdx);
							}
							else bFeedFromMacro = false;
						}
						else pToken = pMacro->GetToken(nMacroIdx);
					}

					switch(bFeedFromMacro ? pToken->GetType() : StoreIdentifierValue(expr))
					{
					case TOKEN_VALUE:
						val = pToken->GetIntValue();
						expr += strlen(pToken->GetString());
						break;
					case TOKEN_CONSTANT:
						break;
					case TOKEN_TYPENAME:
						if(bFeedFromMacro) val = pToken->GetIntValue();
						else val = s_pDeclaredVarType->IsExtended()	? s_pDeclaredVarType->Extend()->GetInternalID().m_wIdx
																	: s_pDeclaredVarType->Standardise()->GetInternalID().m_nIdx;
						expr += strlen(pToken->GetString());
						break;
					case TOKEN_LABEL:
						val = bFeedFromMacro ? pToken->GetLabelValue()->GetOffset() : s_pLabel->GetOffset();
						expr += pToken->GetLabelValue()->GetNameLength();
						break;
					case TOKEN_VAR:
						val = bFeedFromMacro ? pToken->GetVariableValue()->GetIndex() : s_pVariable->GetIndex();
						expr += pToken->GetVariableValue()->GetNameLength();
						break;
					case TOKEN_COMMAND:
						val = bFeedFromMacro ? pToken->GetCommandValue()->GetId() : s_pCommand->GetId();
						expr += pToken->GetCommandValue()->GetNameLength();
						break;
					case TOKEN_MACRO:
						if(!s_pMacro->IsDeclared() || s_pMacro->GetNumTokens() != 1)
							throw("Invalid integer constant expression");
						
						if(!bFeedFromMacro)
						{
							bFeedFromMacro = true;
						}
						else
						{
							s_pMacro->m_pInheritedByMacro = pMacro;
							s_pMacro->m_nInheritedMacroIdx = nMacroIdx;
						}
						pMacro = s_pMacro;
						nMacroIdx = 0;
						continue;
					case TOKEN_STRING:
						{
							expr = strtok(expr, "\n");
							if(auto pConst = CConstants::Get(bFeedFromMacro ? pToken->GetString() : expr))
							{
								expr += pConst->GetNameLength();
								val = pConst->GetValue();
								break;
							}
						}
					default:
						val = StringToInt(expr, expr);
						if(!val && !IsSpace(*expr)) throw("Invalid integer constant expression");
						break;
					}

					//bGotLVal ^= true;
					
					if(bGotLVal)
					{
						rVal = ePreUnary != EXPR_OP_INVALID ? CSyntax::EvaluateExpression(val, ePreUnary) : val;
						lVal = CSyntax::EvaluateExpression(lVal, eOperation, rVal, bLogicalNot, bLogicalOp);
					}
					else
					{
						bGotLVal = true;
						lVal = ePreUnary != EXPR_OP_INVALID ? CSyntax::EvaluateExpression(val, ePreUnary) : val;
					}

					eOperation	= EXPR_OP_INVALID;
					ePreUnary	= EXPR_OP_INVALID;
				}

				bCont = false;
				break;
			}
		}
		while(*expr && *expr != '\n');

		return lVal;
	}

	inline static void ParseConstructElse()
	{
		if(!s_pControlStructure)
			throw("ELSE encountered when not in control structure");
		if(s_pControlStructure->IsLoop())
			throw("Cannot have ELSE in a loop");
		if(s_pControlStructure->GetConstruct() == SCR_CASE)
			s_pLabel = s_pControlStructure->GetBeginLabel();
		
		for(int i=s_nControlStructDepth; i>0; i--) DEBUG_TRACE("\t");
		
		CToken * pToken;
		
		if(s_pControlStructure->GetConstruct() == SCR_CASE)
		{
			// Sort out the default CASE label
			int line = s_pControlStructure->GetLineDeclared(), i;
				//i = CCommands::GetForParsing(SCRIPT_SWITCH)->GetFirstParamIndexOfExtendedType(ET_LABEL);

			// Scan for first label
			for(i=0; (pToken = GetToken(line, i+1)) && pToken->GetType() != TOKEN_LABEL; ++i);
				
			// enable default case
			pToken = GetToken(line,  i);
			pToken->SetValue(1);
			pToken->SetString(s_pCurrentToken);

			// write the GOTO
			auto pCommand = CCommands::GetForParsing(SCRIPT_GOTO);
			pToken = RegisterToken(TOKEN_COMMAND, pCommand->GetName());
			pToken->SetValue(pCommand);
			
			// set the else case label
			auto pLabel = s_pControlStructure->GetBeginLabel();
			pToken = GetToken(line, i + 1);
			pToken->SetValue(pLabel);
			pToken->SetString(pLabel->GetName());

			// We're gonna jump to the end (even if the end is the next line.. silly R*)
			auto pNexLabel = s_pControlStructure->GetEndLabel();
			pToken = RegisterToken(TOKEN_LABEL, pNexLabel->GetName());
			pToken->SetValue(pNexLabel);
			pToken->SetValueType(CSyntax::GetExtendedType(ET_LABEL));
			TranslateToken(pToken);

			// Create the else label now
			NextLine();
			pToken = RegisterToken(TOKEN_LABEL, pLabel->GetName());
			pToken->SetValue(pLabel);
			pToken->IsSkippedDuringCompilation(true);
			pLabel->SetLineNum(s_nNumCodeLines);
		}
		else
		{
			pToken = RegisterToken(TOKEN_COMMAND, s_pCurrentToken);
			pToken->SetValue(CCommands::GetForParsing(SCRIPT_GOTO));

			sprintf(g_szTempString, "__SPECIAL_LABEL_ELSE_%d", s_nNumIFs++);

			auto s_pElseLabel = CLabels::Add(g_szTempString, s_nNumCodeLines);
			s_pElseLabel->IsInternal(true);
			s_pControlStructure->SetEndLabel(s_pElseLabel);

			pToken = RegisterToken(TOKEN_LABEL, s_pCurrentToken);
			pToken->SetValue(s_pElseLabel);
			pToken->SetValueType(CSyntax::GetExtendedType(ET_LABEL));
			TranslateToken(pToken);
		}

		NextLine();
		s_pLabel->SetLineNum(s_nNumCodeLines);
	}
	inline static void ParseConstructEnd()
	{
		CToken * pToken;
		if(!s_pControlStructure)
		{
			switch(s_nConstructID)
			{
			case SCR_ENDWHILE: throw("ENDWHILE encountered when not in WHILE control structure");
			case SCR_ENDREPEAT: throw("ENDREPEAT encountered when not in REPEAT control structure");
			case SCR_ENDCASE: throw("ENDCASE encountered when not in CASE control structure");
			case SCR_ENDIF: throw("ENDIF encountered when not in IF control structure");
			}
		}
		if(s_pControlStructure->GetConstruct() == SCR_CASE && s_nConstructID == SCR_ENDCASE && !s_nNumSwitchCases)
			THROW("No labels found for jump table (declared on line %d)", s_pControlStructure->GetLineDeclared());
		if(s_pControlStructure->IsLoop())
		{
			s_pLabel = s_pControlStructure->GetBeginLabel();
			for(int i=s_nControlStructDepth-1; i>0; i--) DEBUG_TRACE("\t");
			pToken = RegisterToken(TOKEN_COMMAND, s_pCurrentToken);
			pToken->SetValue(CCommands::GetForParsing(SCRIPT_GOTO));
			pToken = RegisterToken(TOKEN_LABEL);
			pToken->SetValue(s_pLabel);
			NextLine();
			s_pLabel = s_pControlStructure->GetEndLabel();
			s_pLabel->SetLineNum(s_nNumCodeLines);
		}
		else
		{
			if(s_pControlStructure->GetConstruct() == SCR_CASE)
			{
				s_pLabel = s_pControlStructure->GetEndLabel();
				s_pLabel->SetLineNum(s_nNumCodeLines);
			}
		}

		//if(s_nConstructID == SCR_ENDCASE)
		//	EndJumpTable();

		s_pControlStructure = s_pControlStructure->GetParent();
		s_bIsInControlStruct = false;
		s_nControlStructDepth--;
	}
	inline static void CreateInternalLabel(const char * szName)
	{
		s_pLabel = CLabels::Add(szName, s_nNumCodeLines);
		s_pLabel->IsHex(s_bIsInHexStruct);
		s_pLabel->InAsm(s_bIsInAsmStruct);
		s_pLabel->IsInternal(true);
	}
	inline static void CreateLabelForIF()
	{
		sprintf(g_szTempString, "__SPECIAL_LABEL_IFS_%d", s_nNumIFs++);
		CreateInternalLabel(g_szTempString);
	}
	inline static void CreateLabelForREPEAT()
	{
		sprintf(g_szTempString, "__SPECIAL_LABEL_REPEATS_%d", s_nNumREPEATs++);
		CreateInternalLabel(g_szTempString);
		s_pControlStructure->SetBeginLabel(s_pLabel);
		auto pToken = RegisterToken(TOKEN_LABEL, g_szTempString);
		pToken->SetValue(s_pLabel);
		pToken->IsSkippedDuringCompilation(true);
		NextLine();
	}
	inline static void CreateControlStructIF(CLabel * pLabel)
	{
		s_pControlStructure = CControlStructs::Add(SCR_IF, s_nControlStructDepth, s_pControlStructure);
		s_pControlStructure->SetEndLabel(pLabel);
		s_pControlStructure->IsLoop(false);
		s_bIsInControlStruct = true;
	}
	inline static void CreateControlStructWHILE(CLabel * pLabel)
	{
		s_pControlStructure = CControlStructs::Add(SCR_WHILE, s_nControlStructDepth, s_pControlStructure);
		s_pControlStructure->SetBeginLabel(s_pLabel);
		s_pControlStructure->IsLoop(true);
		s_bIsInControlStruct = true;
	}
	inline static void CreateControlStructREPEAT(CVariable * pVar, const char * szCount)
	{
		s_pControlStructure = CControlStructs::Add(SCR_REPEAT, s_nControlStructDepth, s_pControlStructure);
		s_pControlStructure->IsLoop(true);
		s_nControlStructDepth++;
		s_bIsInControlStruct = true;

		if(auto pToken = FetchArrayIndex())
		{
			if(pToken->GetType() == TOKEN_VAR)
			{
				auto VAR = s_bWeAreBeingFedByMacro ? s_pToken : new CToken(TOKEN_VAR, pVar->GetName());
				VAR->SetValue(pVar);
				VAR->SetValueType(pVar->GetType());
				VAR->SetSubToken(pToken);
				s_pControlStructure->SetToken(VAR, 0);
			}
			else s_pControlStructure->SetToken(pToken, 0);
		}
		else
		{
			pToken = s_bWeAreBeingFedByMacro ? s_pToken : new CToken(s_pVariable->IsArray() ? TOKEN_VARINDEX : TOKEN_VAR, s_pVariable->GetName());
			s_pVariable->IsArray() ? pToken->SetValue(pVar->GetIndex()) : pToken->SetValue(pVar);
			pToken->SetValueType(pVar->GetType());
			s_pControlStructure->SetToken(pToken, 0);
		}
		
		auto COUNT = new CToken(TOKEN_VALUE, szCount);
		COUNT->SetValueType(CSyntax::GetType(TYPE_INT));
		COUNT->SetValue(StringToInt(szCount));
		s_pControlStructure->SetToken(COUNT, 1);
	}
	inline static void CreateControlStructCASE()
	{
		s_pControlStructure = CControlStructs::Add(SCR_CASE, s_nControlStructDepth++, s_pControlStructure);
		s_pControlStructure->SetLineDeclared(s_nNumLinesParsed);
		s_pControlStructure->IsLoop(false);
		s_bIsInControlStruct = true;
		++s_nSwitchDepth;
	}
	inline static void CreateCodeForIF()
	{
		if(auto pCommand = CCommands::GetForParsing(SCRIPT_ANDOR))
		{
			auto pToken = RegisterToken(TOKEN_COMMAND, s_pCurrentToken);
			pToken->SetValue(CCommands::GetForParsing(SCRIPT_ANDOR));
			s_pControlStructure->SetToken(pToken, 1);
			pToken = RegisterToken(TOKEN_VALUE);
			pToken->SetValueType(CSyntax::GetType(TYPE_INT));
			s_pControlStructure->SetToken(pToken, 0);

			// We're done with this line already
			NextLine();
		}
	}
	inline static void CreateCodeForENDIF()
	{
		auto pLabel = s_pControlStructure->GetEndLabel();
		pLabel->SetLineNum(s_nNumCodeLines);

		TranslateToken(s_pControlStructure->GetToken(0));
		
		auto pToken = RegisterToken(TOKEN_LABEL, pLabel->GetName());
		pToken->SetValue(pLabel);
		pToken->SetValueType(CSyntax::GetExtendedType(ET_LABEL));
		pToken->IsSkippedDuringCompilation(true);
		TranslateToken(pToken);
		NextLine();
	}
	inline static void CreateCodeForREPEAT()
	{
		auto pToken = RegisterToken(TOKEN_COMMAND, s_pCommand->GetName());
		pToken->SetValue(s_pCommand);
		pToken->IsSkippedDuringCompilation(true);
		NextLine();

		// VAR (=) 0
		auto VAR = s_pControlStructure->GetToken(0);
		auto pOperator = COperators::Get("=");
		pToken = RegisterToken(TOKEN_OPERATOR, pOperator->GetOperatorString());
		pToken->SetValue(pOperator->GetOperationMatchingTypes(VAR->GetArgType(), CSyntax::GetType(TYPE_INT)));

		// (VAR) = 0
		AddToken(VAR);

		// VAR = (0)
		pToken = RegisterToken(TOKEN_VALUE, "0");
		pToken->SetValue(0);
		pToken->SetValueType(CSyntax::GetType(TYPE_INT));

		// Translate 'em
		TranslateToken(VAR);
		TranslateToken(pToken);

		// \n
		NextLine();
	}
	inline static void CreateCodeForENDREPEAT()
	{
		// (++)VAR
		auto VAR = s_pControlStructure->GetToken(0);
		auto pOperator = COperators::Get("+=");
		auto pToken = RegisterToken(TOKEN_OPERATOR);
		pToken->SetValue(pOperator->GetOperationMatchingTypes(VAR->GetArgType(), CSyntax::GetType(TYPE_INT)));

		// ++(VAR)
		AddToken(VAR);

		// VAR += *(1)*
		pToken = RegisterToken(TOKEN_VALUE, "1");
		pToken->SetValue(1);
		pToken->SetValueType(CSyntax::GetType(TYPE_INT));
		TranslateToken(pToken);
		NextLine();

		// VAR (>=) COUNT
		int COUNT = s_pControlStructure->GetToken(1)->GetIntValue();
		pOperator = COperators::Get(">=");
		pToken = RegisterToken(TOKEN_OPERATOR);
		pToken->SetValue(pOperator->GetConditionMatchingTypes(VAR->GetArgType(), CSyntax::GetType(TYPE_INT)));
		
		// (VAR) >= COUNT
		AddToken(VAR);
		
		// VAR >= (COUNT)
		AddToken(s_pControlStructure->GetToken(1));
		TranslateToken(s_pControlStructure->GetToken(1));
		NextLine();

		// (GOTO_IF_FALSE) __SPECIAL_LABEL_REPEATS_%d
		pToken = RegisterToken(TOKEN_COMMAND, CCommands::GetForParsing(SCRIPT_ELSE_GOTO)->GetName());
		pToken->SetValue(CCommands::GetForParsing(SCRIPT_ELSE_GOTO));

		// GOTO_IF_FALSE (__SPECIAL_LABEL_REPEATS_%d)
		pToken = RegisterToken(TOKEN_LABEL, s_pControlStructure->GetBeginLabel()->GetName());
		pToken->SetValue(s_pControlStructure->GetBeginLabel());
		pToken->SetValueType(CSyntax::GetExtendedType(ET_LABEL));
		TranslateToken(pToken);
		NextLine();
	}
	inline static void CreateCodeForWHILE()
	{
		auto pCommand = CCommands::GetForParsing(SCRIPT_WHILE);
		auto pToken = RegisterToken(TOKEN_COMMAND, pCommand->GetName());
		pToken->SetValue(pCommand);
		pToken->IsSkippedDuringCompilation(true);
		NextLine();

		// (__SPECIAL_LABEL_WHILES_A_%d:)\n
		pToken = RegisterToken(TOKEN_LABEL, g_szTempString);
		pToken->SetValue(s_pControlStructure->GetBeginLabel());
		pToken->IsSkippedDuringCompilation(true);
		NextLine();

		// (IF)
		CreateCodeForIF();
	}
	inline static void CreateCodeForENDWHILE()
	{
		s_pCommand = CCommands::GetForParsing(SCRIPT_GOTO);
		auto pToken = RegisterToken(TOKEN_COMMAND, s_pCommand->GetName());
		pToken->SetValue(s_pCommand);
		pToken = RegisterToken(TOKEN_LABEL, s_pControlStructure->GetBeginLabel()->GetName());
		pToken->SetValue(s_pControlStructure->GetBeginLabel());
		pToken->SetValueType(CSyntax::GetExtendedType(ET_LABEL));
		TranslateToken(pToken);
		NextLine();
		pToken = RegisterToken(TOKEN_LABEL, s_pControlStructure->GetEndLabel()->GetName());
		pToken->SetValue(s_pControlStructure->GetEndLabel());
		pToken->IsSkippedDuringCompilation(true);
		NextLine();
	}
	inline static void CloseControlStructure()
	{
		s_pControlStructure = s_pControlStructure->GetParent();
		s_bIsInControlStruct = s_pControlStructure ? true : false;
		--s_nControlStructDepth;
	}
	/*inline static void EndJumpTable()
	{
		auto pt = CSyntax::GetType(TYPE_INT);

		// Get number of cases in initial JT command, then the number in the extending command
		int n1 = CCommands::GetForParsing(SCRIPT_CASE)->GetNumCompatibleArgsStrict(pt) - 1,
			n2 = CCommands::GetForParsing(SCRIPT_WHEN)->GetNumCompatibleArgsStrict(pt);

		// Get the max number of 'cases' commands
		int l = (int)ceil((float)(CLimits::GetMaxSwitchSize() - n1 + 2) / (float)n2);

		// Get the number of needed 'cases' commands
		int n = (int)ceil((float)(s_nNumSwitchCases - n1) / (float)n2);

		for(int i=n; i<l; i++)
		{
			ClearTokensOnLine(s_pControlStructure->GetLineDeclared() + i);
		}

		// Set the total number of cases in the initial JT command
		s_pControlStructure->GetToken(0)->SetValue(s_nNumSwitchCases);
		auto pToken = s_pControlStructure->GetToken(0);

		
		// GOTO... here
		s_pLabel = s_pControlStructure->GetEndLabel();
		pToken = RegisterToken(TOKEN_COMMAND);
		pToken->SetValue(CCommands::GetForParsing(SCRIPT_GOTO));
		pToken = RegisterToken(TOKEN_LABEL, s_pLabel->GetName());
		pToken->SetValue(s_pLabel);
		pToken->SetValueType(CSyntax::GetExtendedType(ET_LABEL));
		TranslateToken(pToken);
		NextLine();

		// Place the end label here (for CorrectOffsets to correct the code position)
		pToken = RegisterToken(TOKEN_LABEL, s_pLabel->GetName());
		pToken->SetValue(s_pLabel);
		pToken->IsSkippedDuringCompilation();
		NextLine();
	}*/

	inline static void ConvertTokenType(CToken * pToken, const CTypeBase * pDestArgType)
	{
		auto pArgType = pToken->GetArgType();
		if(pToken->GetType() == TOKEN_VALUE && !pArgType->IsExtended() && !pDestArgType->IsExtended())
		{
			if(pArgType->GetBasicType() == PT_INT)
			{
				if(pDestArgType->GetBasicType() == PT_FLOAT)
				{
					pToken->SetValueType(CSyntax::GetType(TYPE_FLOAT));
					pToken->SetValue((float)pToken->GetIntValue());
				}
			}
			else if(pArgType->GetBasicType() == PT_FLOAT)
			{
				if(pDestArgType->GetBasicType() == PT_INT)
				{
					pToken->SetValueType(CSyntax::GetType(TYPE_INT));
					pToken->SetValue((int)pToken->GetFloatValue());
					Warning("conversion from float to int (possible loss of data)");
				}
			}
		}
	}

	static CToken * FetchArrayIndex()
	{
		CToken * pToken;
		int arsize, nResult;
		char *szVarName;
		if(s_bWeAreBeingFedByMacro) return nullptr;
		if(nResult = CSyntax::GetArrayIndexAndTokenize(s_pLineTokens[s_nNumTokens-1], arsize, szVarName))
		{
			if(!s_pVariable->IsArray()) szVarName ? throw("Variable indexes can only be used on arrays") : throw("Constant indexes can only be used on arrays");
			if(nResult && SCRambl.Config.Parser.UseOneBasedIndex()) arsize--;
			if(szVarName)
			{
				if(auto pVariable = g_pScript->FindVariable(szVarName))
				{
					if(pVariable->GetType()->GetBasicType() != PT_INT)
						THROW("Cannot use a %s as an array index", pVariable->GetType()->GetName());
					
					if(pVariable->GetIndex() > MAX_VAR_INDEX) throw("Var index is too big to be used as an array index");

					pToken = new CToken(TOKEN_VAR, szVarName);
					pToken->SetValue(pVariable);
					pToken->SetValueType(pVariable->GetType());
					return pToken;
				}
				else THROW("Unexpected identifier '%s'", szVarName);
			}
			else
			{
				if(arsize < 0 || arsize > s_pVariable->GetArraySize())
					throw("Array index out of bounds");
				if(s_pVariable->GetIndex() + arsize * 4 > CLimits::GetMaxVar() * 4)
					throw("Constant index pushes the variable out of bounds");

				if(!s_bWeAreBeingFedByMacro)
					pToken = new CToken(TOKEN_VARINDEX, s_pCurrentToken);
				else
				{
					pToken = s_pToken;
					pToken->SetType(TOKEN_VARINDEX);
				}

				pToken->SetValue(s_pVariable->GetIndex() + arsize * 4);
				pToken->SetValueType(s_pVariable->GetType());
				return pToken;
			}
		}
		return nullptr;
	}

	static void Error(const char *szStr)						{Printf("ERROR: %s on line %d", szStr, s_nNumCodeLines);}

protected:
	static CToken					*	s_pTokens[MAX_LINES][IDENTIFIERS_PER_LINE];
	static DWORD						s_dwLineOffsets[MAX_LINES];

	static const char				*	s_pTypeName;
	static CScript					*	s_pScript;
	static CSubScript				*	s_pSubScript;
	static CScriptFile				*	s_pScriptFile;
	static const CCommand			*	s_pCommand;
	static CCommandArg				*	s_pCommandArg;
	static CVariable				*	s_pVariable;
	static CLabel					*	s_pLabel;
	static COperator 				*	s_pOperator;
	static COperation				*	s_pOperations[MAX_INLINE_OPERATIONS];
	static CControlStruct			*	s_pControlStructure;
	static CMacro					*	s_pMacro;
	static CMacro					*	s_pDeclaredMacro;
	static CMacroParam				*	s_pMacroParam;

	// Register a token for parsed code data
	static CToken * RegisterToken(eTokenType type, const char * szString = nullptr)
	{
		auto pToken = new CToken(type, szString);
		AddToken(pToken);
		return pToken;
	}
	// Add token to the collection
	static void AddToken(CToken *pToken)
	{
		// If we're in a function-like macro, pass the token to it for use later
		if(s_bWeAreBeingFedByMacro && s_pMacro && s_pMacro->GetNumParams())
		{
			if(s_pMacro->GetNumParams() > s_nNumMacroParamsPassed)
			{
				++s_nNumMacroParamsPassed;
				s_pMacro->GiveArg(pToken);
				if(s_nNumMacroParamsPassed >= s_pMacro->GetNumParams())
					s_bExpectAnything = false;
				return;
			}
		}

		// If we're declaring a macro, we can lazily add the token, which should be empty apart from the token text and type
		if(s_bIsDeclaringMacro)
		{
			// Append the token to the macro
			s_pDeclaredMacro->AddToken(pToken);
		}
		else
		{
			// Try to prevent line overflow (fixes may need adding to allow 'line wrapping' in parsing)
			if(s_nNumRegisteredTokens >= MAX_TOKENS)
			{
				++s_nNumLinesParsed;
				s_nNumRegisteredTokens = 0;
			}

			// Append the token to the code
			s_pTokens[s_nNumLinesParsed][s_nNumRegisteredTokens++] = pToken;
		}
	}
	static void ClearTokensOnLine(int line)
	{
		s_dwLineOffsets[line] = 0;

		for(int i=0; i<MAX_TOKENS; i++)
		{
			if(s_pTokens[line][i])
			{
				delete s_pTokens[line][i];
				s_pTokens[line][i] = nullptr;
			}
		}

		s_nNumRegisteredTokens = 0;
	}
	static CToken * RecallToken(int index)
	{
		return index < MAX_TOKENS ? s_pTokens[s_nNumLinesParsed][index] : nullptr;
	}
	static void SetToken(int index, CToken *pToken)
	{
		if(index < MAX_TOKENS) s_pTokens[s_nNumLinesParsed][index] = pToken;
	}
	static CToken * GetToken(int line, int index)
	{
		return (line < MAX_LINES && index < MAX_TOKENS) ? s_pTokens[line][index] : nullptr;
	}

	static void NextLine()
	{
		s_nNumLinesParsed++;
		s_nNumRegisteredTokens = 0;
		ClearTokensOnLine(s_nNumLinesParsed);
	}
	static void RewindLine()
	{
		s_nNumRegisteredTokens = 0;
	}
	static int GetNumRegisteredTokens()
	{
		return s_nNumRegisteredTokens;
	}

public:
	static void Reset()
	{
		// Clear all script identifiers
		//CLabels::Reset();
		//CVariables::Reset();
		CConstants::Reset();
		CCommands::Reset();
		CControlStructs::Reset();
		CMacros::Reset();
	}
	static const CToken * GetTokenReadOnly(int line, int index)
	{
		return (line < MAX_LINES && index < MAX_TOKENS) ? s_pTokens[line][index] : nullptr;
	}

	static int							s_nNumLinesParsed;
	static int							s_nPushedLinesParsed;
	static int							s_nPushedRegisteredTokens;
	static void							SetTokenToConstant(CToken *, const CConstant<int> *);
	static const CConstant<int>		*	GetConstant(CToken *, const CTypeBase *);
	static const CConstant<int>		*	GetConstant(const char *);
	static const CEnumerator		*	GetEnumerator(CToken *, CToken *, int);
	static void							ValidateString(CToken *, const CType *);
	static int							Parse(CSubScript *);
	static void							Parse(CScriptFile *);
	static int							Preparse(CScriptFile * = nullptr, CScript * = nullptr, bool = false);
	static void							ValidateStringToken(CToken *, const CType *, CToken * = nullptr, int = -1);
	static void							Warning(const char *szStr)			{Printf("WARNING: %s on line %d", szStr, s_nNumCodeLines);}
};